/*
Sample:

jQuery('.tables').sortableTable({
	order: 'asc', 
	sortBy: 0,
	tieBreaker: 1, // in case sortBy column is drawn
	specialCollumn: { // optional
		index: 1, // column index
		orderBy: ['0','2','1'] // tell it how you wanna order this collumn
	},
	handler: 'click',
	noHandlerSelector: '.no-handler'
}).each(function() {
	// rescue instances and use its methods
	$(this).data('sortableTable').setOrder('desc').sortBy(1).setTableHeaderStyle('1', 'desc');
});
*/

(function ($) {

	var SortableTable = function (el, settings, callback) {
	
		var self = this;
		
		//SETTINGS
		settings = settings || {};
		settings.order = settings.order || 'asc';
		settings.tieBreaker = settings.tieBreaker || null;
		settings.handler = settings.handler || 'click';
		settings.noHandlerSelector = settings.noHandlerSelector || '.no-handler';
		
		// OBJECTS
		self.obj = $(el);
		
		self.obj.thead = self.obj.find('thead');
		self.obj.tbody = self.obj.find('tbody');
		self.obj.ths = self.obj.thead.find('th');
		self.obj.trs = self.obj.tbody.find('tr');		
		
		// METHODS
		
		self.setOrder = function (val) {
			self.obj.order = val;
			return self;
		};
		
		self.sortBy = function (index) {
		
			var rows = [],
				i = 0,
				total = self.obj.trs.length,
				html = '';
				
			// extract rows from DOM	
			self.obj.trs.each(function () {
				var el = $(this);
				rows.push(el);
			});
			
			// order by collumn
			rows.sort(function (a, b) {
			
				var x = $.trim(a.find('>th, >td').filter(':eq(' + index + ')').text()),
					y = $.trim(b.find('>th, >td').filter(':eq(' + index + ')').text());
				
				// drawn?
				if (x === y && settings.tieBreaker !== null) {
				
					x = a.find('>th, >td').filter(':eq(' + settings.tieBreaker + ')').text();
					y = b.find('>th, >td').filter(':eq(' + settings.tieBreaker + ')').text();
					
					// in case tie breaker is a special columns
					if (settings.specialCollumn != undefined) {
						if (typeof (settings.specialCollumn.orderBy) === 'object') {
							x = $.inArray(x, settings.specialCollumn.orderBy);
							y = $.inArray(y, settings.specialCollumn.orderBy);
						}
					}
				
				}
				
				// special columns, special order
				if (settings.specialCollumn != undefined) {
					if (index === settings.specialCollumn.index && typeof (settings.specialCollumn.orderBy) === 'object') {
						x = $.inArray(x, settings.specialCollumn.orderBy);
						y = $.inArray(y, settings.specialCollumn.orderBy);
					}
				}
				
				// numeric values
				if ((x == parseInt(x, 10)) && (y == parseInt(y, 10))) {
					
					return x - y;
				
				// string values
				} else {
					return (x < y) ? -1 : ((x > y) ? 1  : 0);
				}
			
			});
			
			// define order
			if (self.obj.order === 'desc') {
				rows = rows.reverse();
			}
			
			// mount HTML
			for (i; i < total; i++) {
				html += '<tr class="' + rows[i].attr('class') + '">' + rows[i].html() + '</tr>';
			}
			
			self.obj.tbody.html('').append(html);
			
			// stripes
			self.obj.tbody.find('tr').addClass('even').removeClass('odd').filter(':nth-child(odd)').removeClass('even').addClass('odd');
			
			// callback
			if (typeof callback == 'function') {
				callback.apply(self);
			}
			
			return self;
			
		};
		
		self.setTableHeaderStyle = function (columnIndex, cssClass) {
			
			self.obj.ths.removeClass('asc').removeClass('desc');
			self.obj.ths.filter('th:eq(' + columnIndex + ')').addClass(cssClass);
			
			return self;
		
		};
		
		// BIND HANDLER
		
		if (settings.handler) {
		
			self.obj.ths.not(settings.noHandlerSelector).bind(settings.handler, function (evt) {
				evt.preventDefault();
			
				var el = $(this),
					index = self.obj.ths.index(el), // get collumn
					colspans = 0,
					order = $(this).data('order') || settings.order; // get order
					
				// consider colspan on ths
				el.prevAll().each(function () {
					colspans += parseInt($(this).attr('colspan'), 10) - 1;
				});
				
				// style
				self.setTableHeaderStyle(index, order);
				
				// setters
				self.setOrder(order);
				self.sortBy(index + colspans);
				
				// invert the order
				$(this).data('order', (order === 'asc') ? 'desc' : 'asc');
			
			});
		
		}
		
		// INIT
		if (typeof settings.sortBy == 'number') {
			self.obj.ths.filter(':nth(' + settings.sortBy + ')').trigger(settings.handler);
		}
		
	
	};

	// EXTEND JQUERY
	
	$.fn.sortableTable = function (settings, callback) {
		return this.each(function () {
		
			var obj = $(this),
				instance;
			
			if (obj.data('sortableTable')) {
				return;
			}
			
			instance = new SortableTable(this, settings, callback);
			obj.data('sortableTable', instance);
		
		});
	};

}(jQuery));